﻿using System;

namespace SL5.Common
{
	/// <summary>
	/// Contains methods to help with converting objects to different types.
	/// </summary>
	public static class ConverterHelper
	{
		#region Public Static Methods 
		/// <summary>
		/// Attempts to convert the specified <paramref name="value"/> to the specified conversion type <typeparam name="T"/>,
		/// and returns a value indicating if the conversion was successful.  An output parameter <paramref name="convertedValue"/>
		/// will hold either the result of the conversion, or the default value of <typeparamref name="T"/>.
		/// </summary>
		/// <typeparam name="T">The conversion type</typeparam>
		/// <param name="value">The value.</param>
		/// <param name="formatProvider">The format provider.</param>
		/// <param name="convertedValue">The converted value.</param>
		/// <returns>A value indicating if the conversion was successful.</returns>
		public static bool TryConvert<T>(object value, IFormatProvider formatProvider, out T convertedValue)
		{
			object convertedObject;
			if (ConverterHelper.TryConvert(value, typeof(T), formatProvider, out convertedObject))
			{
				convertedValue = (T)convertedObject;
				return true;
			}
			convertedValue = default(T);
			return false;
		}

		/// <summary>
		/// Attempts to convert the specified <paramref name="value"/> to the <paramref name="conversionType"/>,
		/// and returns a value indicating if the conversion was successful.  An output parameter <paramref name="convertedValue"/>
		/// will hold either the result of the conversion, or <c>null</c> if the conversion failed.  Obviously, for some types,
		/// <c>null</c> is a valid conversion result, hence the boolean return value.
		/// </summary>
		/// <param name="value">The value.</param>
		/// <param name="conversionType">Type of the conversion.</param>
		/// <param name="formatProvider">The format provider.</param>
		/// <param name="convertedValue">The converted value.</param>
		/// <returns>A value indicating if the conversion was successful.</returns>
		public static bool TryConvert(object value, Type conversionType, IFormatProvider formatProvider, out object convertedValue)
		{
			if (conversionType == null)
			{
				throw new ArgumentNullException("conversionType");
			}
			if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition() == typeof(Nullable<>))
			{
				if (value == null)
				{
					convertedValue = null;
					return true;
				}
				return ConverterHelper.TryConvert(value, conversionType.GetGenericArguments()[0], formatProvider, out convertedValue);
			}
			if (value == null)
			{
				convertedValue = null;
				return !conversionType.IsValueType;
			}
			if (conversionType.IsAssignableFrom(value.GetType()))
			{
				convertedValue = value;
				return true;
			}
			if (conversionType.IsEnum && Enum.IsDefined(conversionType, value))
			{
				string text = value as string;
				convertedValue = text != null ? Enum.Parse(conversionType, text, false) : Enum.ToObject(conversionType, value);
				return true;
			}
			if (typeof(IConvertible).IsAssignableFrom(conversionType))
			{
				IConvertible source = value as IConvertible;
				if (source != null)
				{
					try
					{
						convertedValue = source.ToType(conversionType, formatProvider);
						return true;
					}
					catch (Exception)
					{
						// Do nothing.
					}
				}
			}
			if (conversionType == typeof(string))
			{
				convertedValue = value.ToString();
				return true;
			}
			convertedValue = null;
			return false;
		}
		#endregion Public Static Methods 
	}
}